#include "GameCommander.h"

//Initializes all the modules
bool GAME::Initialize(HWND hWnd,HINSTANCE hInst,HINSTANCE hPrevInst,int nCmdShow,LPSTR lpCmdLine)
{
	assert(hWnd != NULL);
	m_hWnd = hWnd;
	assert(hInst != NULL);
	assert(hPrevInst == NULL);
	assert(lpCmdLine != NULL);
	
	if(!Pen::Engine()->Initialize(hWnd))
	{PostQuitMessage(0);return false;}

	if(!Input::Engine()->Init(hWnd,hInst))
	{PostQuitMessage(0);return false;}

	if(!Cursor::Engine()->Init(hWnd,Pen::Engine()->GetClientRect()))
	{PostQuitMessage(0);return false;}

	if(!Maze::Engine()->Init())
	{PostQuitMessage(0);return false;}

	/*if(!Speaker::Engine()->Initialize())
	{PostQuitMessage(0);return false;}*/

	if(!Video::Engine()->Init(hWnd))
	{PostQuitMessage(0);return false;}

	srand(unsigned(time(0)));

	Objects3D->Init();
	ObjectsSprite->Init();
	Remove3D->Init();


	m_Sidebar = new Menu();
	ObjectsSprite->add(((BaseObject*)(m_Sidebar)));

	m_Player = new Character(15.0f,15.0f,0.0f);
	Objects3D->add(((BaseObject*)(m_Player)));
#ifdef IMPLEMENT_SWORD
	Objects3D->add(((BaseObject*)(m_Player->AccessWeapon())));
#endif

	Maze::Engine()->CreateMap((int)WORLD_HEIGHT / 2,(int)WORLD_WIDTH / 2);
	Maze::Engine()->SetListInfo(Objects3D);
	GameState = INIT_STATE;

	return true;
}


void GAME::CleanUp()
{
	Input::Engine()->CleanUp();
	/*
	Pen::Engine()->CleanUp();
	Messenger::Engine()->CleanUp();
	Maze::Engine()->CleanUp();
	*/
	Objects3D->Clear();
	ObjectsSprite->Clear();
}

//Resets and rerandomizes the world for a new level
//Clears out all old items, coins, enemies, end marker
//Resets the players position to the starting location
void GAME::RESETWORLD()
{
	Objects3D->Clear();
	
	m_Player->Reset();

	Maze::Engine()->Reset();

	Objects3D->add(((BaseObject*)(m_Player)));
#ifdef IMPLEMENT_SWORD
	Objects3D->add(((BaseObject*)(m_Player->AccessWeapon())));
#endif

	Maze::Engine()->CreateMap((int)WORLD_WIDTH/2,(int)WORLD_HEIGHT/2);
	Maze::Engine()->SetListInfo(Objects3D);
}

//Draws everthing on screen, attempts to catch and send an error message if there is nothing in the
// drawlist
void GAME::DRAW()
{
	if(!Pen::Engine()->Draw(Objects3D,ObjectsSprite))
	{
		MessageBox(0, "Nothing to draw. Probably pointer errors. Exiting program for security...",
			"Error", 0);
		PostQuitMessage(0);
	}
}

//Updates all objects currently in the game
void GAME::UPDATE()
{
	//Does a basic update of all sprites
	// Menu
	for(int i = 0;i < ObjectsSprite->getSize();++i)
	{
		ObjectsSprite->get(i)->Update(0);
	}
	//Does specific updates for each type of object
	for(int i = 0;i < Objects3D->getSize();++i)
	{
		switch(Objects3D->get(i)->m_Type)
		{
		case ENEMY_TYPE:
			Objects3D->get(i)->Update((void*)m_Player);
			if(((Enemy*)(Objects3D->get(i)))->IfDied())
				Remove3D->add(Objects3D->get(i));
			break;
		case ITEM_TYPE:
			Objects3D->get(i)->Update(0);
			if(((Pickup*)(Objects3D->get(i)))->ifFound())
				Remove3D->add((BaseObject*)(Objects3D->get(i)));
			break;
		case PLAYER_TYPE:
			m_Player->SendStats(m_Sidebar->m_iID);
			if(m_Player->IfDied())
				LOST(0);
		default:
			Objects3D->get(i)->Update(0);
			break;
		}
	}
	//This handles any objects that need to be removed from game.
	while(Remove3D->getSize() != 0)
	{
		BaseObject* ToRemove = Remove3D->get(0);
		Objects3D->remove(ToRemove);
		Remove3D->remove(ToRemove);
		if(Remove3D->getSize() == 0)
			Remove3D->Clear();
	}	
}


//Handles Character input menu input and sends data to the enemies as time only
// this allows them to update their AI
void GAME::INPUT()
{
	if(TICK(CHECKTIME))
	{
		Input::Engine()->CharacterPoll(m_Player,Objects3D,ElapsedTime);

		Input::Engine()->AIPoll(Objects3D,ElapsedTime);

		GameState = (eGameStates)Input::Engine()->MenuPoll(ObjectsSprite);
	}
}


//Throttle code to make sure the game does not just run rampant with a cpu
double GAME::CLOCK()
{
	//Initializes now and then
	static double now;
	static double then = timeGetTime();
	//Updates now
	now = timeGetTime();
	//Used to check with
	static double TimeTotal = 0;
	//How long has it been
	// add to total
	double TimeTemp = ( now - then ) * .001f;
	TimeTotal += TimeTemp;
	then = now;
	//If it has been a second, update and reset
	if(TimeTotal >= WAITTIME)
	{
		TICK(SETTIME);
		TimeTotal = 0;
	}
	//Update fps count
	ElapsedTime = TimeTemp;
	/*std::stringstream out;out << "\nTime Elapsed: " << ElapsedTime;
	string s = out.str();
	Pen::Engine()->DebugFont(s);*/
	return TimeTemp;
}

bool GAME::TICK(int Type)
{
	if(Type == SETTIME)
	{
		SetTimeElapsed = true;
		return false;
	}
	if(Type == CHECKTIME)
	{
		bool temp = SetTimeElapsed;
		SetTimeElapsed = false;
		return temp;
	}
	return false;
}

//This works as a basic state machine. Startup, load, run, quit.
void GAME::WORK()
{
	//Updates the time based off wall clock
	CLOCK();
	switch(GameState)
	{
	case INIT_STATE:
		
		if(!Video::Engine()->IsPlaying())
			Video::Engine()->Toggle(true);

		//Wait until the end video button was pressed (ENTER KEY)
		if(Input::Engine()->MoviePoll() || !Video::Engine()->IsPlaying()) 
		{
			Video::Engine()->Toggle(false);
			
			Video::Engine()->ShutDownVideo(m_hWnd);
			GameState = UPDATE_STATE;
		}
		break;
	case UPDATE_STATE:
		//Grabs input and updates objects if it has been the specified time lapse
		INPUT();

		UPDATE();

		//Draws everything whether its updated recently or not
		DRAW();

		

		if(WON(1) || LOST(1))
			RESETWORLD();
		break;
	case QUIT_STATE:
		CleanUp();
		PostQuitMessage(0);
		break;
	default:
		break;
	}
}

//These manage what state the running game is in.
bool GAME::LOST(int L)
{
	static bool ifLOST = false;
	if(L == 0)
	{
		ifLOST = true;
		return false;
	}
	if(L == 1)
	{
		bool temp = ifLOST;
		ifLOST = false;
		return temp;
	}
	return false;
}

bool GAME::WON(int W)
{
	static bool ifWON = false;
	if(W == 0)
	{
		ifWON = true;
		return false;
	}
	if(W == 1)
	{
		bool temp = ifWON;
		ifWON = false;
		return temp;
	}
	return false;
}